package dbservice

import (
	"crypto/md5"
	"fmt"
	"io"
	"strconv"
	"time"

	"github.com/redis/go-redis/v9"
)

/*
DB session 的信息，用来管理 用户对应的dbserver服务器路由信息
*/
type DBsessionUtil struct {
	pid uint64
	Key string
}

// SessionInfo 字段表
type DBSessionInfo struct {
	Token    string `redis:"token"`
	EntityID uint64 `redis:"entityID"`
	// IP         string `redis:"ip"`
	DBServerID uint64 `redis:"DBServerID"`
}

const (
	// SessionPrefix redis key后缀
	dbsessionPrefix = "dbsession"
	// redisEntityID Session表中存放entityID的字段
	DBredisEntityID = "entityID"
	DBredisToken    = "token"
	// DBSID 所在的db服务器
	DBSID = "DBServerID"
)

// var TokenVerifyError = errors.New("Token Error")

// SessionUtil 获得Session表的工具类
func DBSessionUtil(pid uint64) *DBsessionUtil {
	sessUtil := &DBsessionUtil{}
	sessUtil.pid = pid
	sessUtil.Key = fmt.Sprintf("%s:%d", dbsessionPrefix, sessUtil.pid)
	return sessUtil
}

// createToken 方法根据uid和当前时间生成唯一的token
// md5(curtime+uid)
func (util *DBsessionUtil) createToken() string {
	curtime := time.Now().Unix()
	h := md5.New()

	io.WriteString(h, strconv.FormatInt(curtime, 10))
	io.WriteString(h, strconv.FormatUint(util.pid, 10))
	token := fmt.Sprintf("%x", h.Sum(nil))

	return token
}

// SetToken 生成token并将token保存至redis
// 返回token和err
func (util *DBsessionUtil) SetToken() (string, error) {
	t := util.createToken()
	rd := GetEntityRedis()
	defer rd.Dispose()
	if err := rd.HMSet(rd.Ctx, util.Key, DBredisToken, t).Err(); err != nil {
		return "", err
	}
	return t, nil
}

// DelSession 删除Session表
func (util *DBsessionUtil) DelSession(token string) error {
	rd := GetEntityRedis()
	defer rd.Dispose()

	return rd.Watch(rd.Ctx, func(tx *redis.Tx) error {
		if tmptoken := tx.HGet(rd.Ctx, util.Key, DBredisToken).String(); tmptoken != token {
			return TokenVerifyError
		}
		if r, err := tx.HExists(rd.Ctx, util.Key, DBredisEntityID).Result(); r || err != nil {
			return nil
		}
		tx.TxPipelined(rd.Ctx, func(p redis.Pipeliner) error {
			return p.Del(rd.Ctx, util.Key).Err()
		})
		return nil
	}, util.Key)
}

// GetUserEntityID 获得玩家的EntityID
func (util *DBsessionUtil) GetUserEntityID() (uint64, error) {
	rd := GetEntityRedis()
	defer rd.Dispose()

	return rd.HGet(rd.Ctx, util.Key, DBredisEntityID).Uint64()
}

func (this *DBsessionUtil) Expire() {
	rd := GetEntityRedis()
	rd.Expire(rd.Ctx, this.Key, Expire_Time)
}
